﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Common.TextMatch
{
    /// <summary>
    /// 
    /// </summary>
    public class SentenceBoundaryUtil
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="docContent"></param>
        /// <returns></returns>
        public static Dictionary<int, Range<int>> GetSetenceRanges(string docContent)
        {
            Dictionary<int, Range<int>> setences = new Dictionary<int, Range<int>>();
            if (!string.IsNullOrEmpty(docContent))
            {
                int setenceID = 0;
                int charPos = -1;
                int setenceStart = 0;
                string attachedWord = string.Empty;
                foreach (char c in docContent)
                {
                    charPos++;
                    if (c == '?' || c == '!')
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    else if ((c == '"' || c == '\'') && (docContent.Length > charPos + 1 && docContent[charPos + 1] == '.'))
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    //else if (c == ')' || c == ']' || c == '}')
                    //{
                    //    if (charPos > setenceStart)
                    //    {
                    //        Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                    //        setenceID++;
                    //        setences.Add(setenceID, setenceRange);
                    //        setenceStart = charPos + 1;
                    //        attachedWord = string.Empty;
                    //    }
                    //}
                    else if (docContent.Length > charPos + 1 &&
                        (docContent[charPos + 1] == '"' ||
                        docContent[charPos + 1] == '\''))
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    else if (c == ' ' || c == '\n' || c == '\r')
                    {
                        attachedWord = string.Empty;
                    }
                    else if (c == '\r' && docContent.Length > charPos + 1 &&
                        docContent[charPos + 1] == '\n')
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    else if (c == '.')
                    {
                        if (!string.IsNullOrEmpty(attachedWord) && attachedWord.IndexOf('.') >= 0)
                        {
                            attachedWord += c;
                        }
                        else if (docContent.Length > charPos + 1 && docContent[charPos + 1] == '.')
                        {
                            attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                            Char.IsLower(attachedWord[0]) &&
                            docContent.Length > charPos + 1 &&
                            (docContent[charPos + 1] == ' ' || Char.IsUpper(docContent[charPos + 1])))
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                        }
                        else if (string.IsNullOrEmpty(attachedWord) || attachedWord.Length < 2)
                        {
                            attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                            attachedWord.Length < 5 &&
                            docContent.Length > charPos + 1 &&
                            Char.IsUpper(docContent[charPos + 1]))
                        {
                            attachedWord += c;
                        }
                        else
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                        }
                    }
                    else
                        attachedWord += c;
                }
                if (setenceStart < docContent.Length)
                {
                    charPos = docContent.Length;
                    Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                    setenceID++;
                    setences.Add(setenceID, setenceRange);
                }
            }
            return setences;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="docContent"></param>
        /// <returns></returns>
        public static Dictionary<int, Range<int>> GetCitationPhraseRanges(string docContent)
        {
            Dictionary<int, Range<int>> setences = new Dictionary<int, Range<int>>();
            if (!string.IsNullOrEmpty(docContent))
            {
                int setenceID = 0;
                int charPos = -1;
                int setenceStart = 0;
                string attachedWord = string.Empty;
                foreach (char c in docContent)
                {
                    charPos++;
                    if (c == '?' || c == '!')
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    else if ((c == '"' || c == '\'') && (docContent.Length > charPos + 1 && docContent[charPos + 1] == '.'))
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    //else if (c == ')' || c == ']' || c == '}')
                    //{
                    //    if (charPos > setenceStart)
                    //    {
                    //        Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                    //        setenceID++;
                    //        setences.Add(setenceID, setenceRange);
                    //        setenceStart = charPos + 1;
                    //        attachedWord = string.Empty;
                    //    }
                    //}
                    //else if (docContent.Length > charPos + 1 &&
                    //    (docContent[charPos + 1] == '"' ||
                    //    docContent[charPos + 1] == '\''))
                    //{
                    //    if (charPos > setenceStart)
                    //    {
                    //        Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                    //        setenceID++;
                    //        setences.Add(setenceID, setenceRange);
                    //        setenceStart = charPos + 1;
                    //        attachedWord = string.Empty;
                    //    }
                    //}
                    else if (c == ' ' || c == '\n' || c == '\r')
                    {
                        attachedWord = string.Empty;
                    }
                    else if (c == '\r' && docContent.Length > charPos + 1 &&
                        docContent[charPos + 1] == '\n')
                    {
                        if (charPos > setenceStart)
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                    }
                    else if (c == '.')
                    {
                        if (attachedWord.Length >= 2 && attachedWord[attachedWord.Length - 2] == '.' && Char.IsUpper(attachedWord[attachedWord.Length - 1]))
                        {
                            attachedWord += c;
                        }
                        else if (attachedWord.Length == 1 && Char.IsUpper(attachedWord[0]))
                        {
                            attachedWord += c;
                        }
                        else if (docContent.Length > charPos + 1 &&
                            Char.IsNumber(docContent[charPos + 1]))
                        {
                            attachedWord += c;
                        }
                        else if (docContent.Length > charPos + 1 &&
                            Char.IsPunctuation(docContent[charPos + 1]))
                        {
                            attachedWord += c;
                        }
                            //else if (!string.IsNullOrEmpty(attachedWord) &&
                        //    docContent.Length > charPos + 3 &&
                        //    (docContent[charPos + 1] == ' ' &&
                        //    Char.IsUpper(docContent[charPos + 2]) &&
                        //    docContent[charPos + 3] == '.'))
                        //{
                        //    attachedWord += c;
                        //}
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                        docContent.Length > charPos + 2 &&
                        (docContent[charPos + 1] == '\'' ||
                        docContent[charPos + 1] == '"' ||
                        docContent[charPos + 2] == '\'' ||
                        docContent[charPos + 2] == '"'))
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                            else
                                attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                            docContent.Length > charPos + 2 &&
                            (docContent[charPos + 1] == ' ' && Char.IsUpper(docContent[charPos + 2])))
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                            else
                                attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                            docContent.Length > charPos + 2 &&
                            (Char.IsUpper(docContent[charPos + 1]) && Char.IsLetter(docContent[charPos + 2])))
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                            else
                                attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) && attachedWord.IndexOf('.') >= 0)
                        {
                            attachedWord += c;
                        }
                        else if (docContent.Length > charPos + 1 && docContent[charPos + 1] == '.')
                        {
                            attachedWord += c;
                        }
                        else if (string.IsNullOrEmpty(attachedWord) || attachedWord.Length < 2)
                        {
                            attachedWord += c;
                        }
                        else if (!string.IsNullOrEmpty(attachedWord) &&
                            attachedWord.Length < 5 &&
                            docContent.Length > charPos + 1 &&
                            Char.IsUpper(docContent[charPos + 1]))
                        {
                            attachedWord += c;
                        }
                        else
                        {
                            if (charPos > setenceStart)
                            {
                                Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                                setenceID++;
                                setences.Add(setenceID, setenceRange);
                                setenceStart = charPos + 1;
                                attachedWord = string.Empty;
                            }
                        }
                    }
                    else if (c==',' || c==':' || c==';')
                    {
                        Regex numberRegex = new Regex("^\\(?\\[?\\{?\\d+\\)?\\]?\\}?$");
                        Regex volumeIssueRegex = new Regex("^\\d+\\(?\\d+\\)?|\\d+[\\.:]\\d+$");
                        Regex pageRangeRegex = new Regex("^\\d+[-\\s]+\\d+$");
                        if (numberRegex.IsMatch(attachedWord.Trim()))
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                        else if (volumeIssueRegex.IsMatch(attachedWord.Trim()))
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                        else if (pageRangeRegex.IsMatch(attachedWord.Trim()))
                        {
                            Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                            setenceID++;
                            setences.Add(setenceID, setenceRange);
                            setenceStart = charPos + 1;
                            attachedWord = string.Empty;
                        }
                        else
                            attachedWord += c;
                    }
                    else
                        attachedWord += c;
                }
                if (setenceStart < docContent.Length)
                {
                    charPos = docContent.Length;
                    Range<int> setenceRange = new Range<int>(setenceStart, charPos);
                    setenceID++;
                    setences.Add(setenceID, setenceRange);
                }
            }
            return setences;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static List<string> GetSentences(string input)
        {
            Dictionary<int, Range<int>> sentenceRanges = SentenceBoundaryUtil.GetSetenceRanges(input);
            List<string> sentences=new List<string>();
            foreach(int pos in sentenceRanges.Keys)
            {
                Range<int> range = sentenceRanges[pos];
                string sentence = input.Substring(range.FromValue, range.ToValue - range.FromValue);
                sentences.Add(sentence);
            }
            return sentences;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="citationText"></param>
        /// <returns></returns>
        public static List<string> GetCitationPhrases(string citationText)
        {
            Dictionary<int, Range<int>> ranges = SentenceBoundaryUtil.GetCitationPhraseRanges(citationText);
            List<string> phrases=new List<string>();
            foreach(Range<int> range in ranges.Values)
            {
                string phrase = citationText.Substring(range.FromValue, range.ToValue - range.FromValue + 1);
                phrases.Add(phrase);
            }
            return phrases;
        }
    }
}
